package com.chen.qq;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.CellRendererPane;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import com.chen.Shell;
import com.chen.time.timing.Animator;
import com.chen.time.timing.TimingAdapter;

/**
 * @author 5
 * 
 */
public class AnimatePane extends JPanel {

    private static final long serialVersionUID = 2722474171850109405L;

    private CellRendererPane crp = new CellRendererPane();

    private Component current;

    private Component next;

    private float ratio;

    private int duration = 1000;

    private TransformPolicy policy = new ShapePolicy();

    private Animator animator = new Animator(duration, new TimingAdapter() {
        @Override
        public void timingEvent(float fraction) {
            ratio = fraction;
            repaint();
        }

        @Override
        public void begin() {
            current.setVisible(false);
            next.setVisible(false);
        }

        @Override
        public void end() {
            add(current);
            add(next);

            current.setVisible(false);
            next.setVisible(true);

            current = next;
            next = null;
            doLayout();
        }
    });

    public static void main(String[] args) {
        JButton btn = new JButton("btn");
        JPanel pane = new JPanel(new BorderLayout());
        final AnimatePane p = new AnimatePane();
        p.add(new JScrollPane(new JTextField("hello")));
        p.add(new JButton("2222222222222"));
        // p.add(new JTree());
        pane.add(p);
        pane.add(btn, BorderLayout.SOUTH);

        btn.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                p.showNext();
            }
        });

        Shell.run(pane);
    }

    public AnimatePane() {
        this.setLayout(new Layout());
        super.add(crp);
    }

    public void setDuration(int duration) {
        if (animator.isRunning())
            return;

        animator.setDuration(duration);
        this.duration = duration;
    }

    public void setPolicy(TransformPolicy policy) {
        this.policy = policy;
    }

    @Override
    public Component add(Component c) {
        if (getComponentCount() > 1) {
            c.setVisible(false);
        } else {
            current = c;
        }
        return super.add(c);
    }

    @Override
    public void remove(int index) {
        if (index == 0)
            return;

        super.remove(index);
    }

    public void show(Component c) {
        if (c == null || current == c)
            return;

        next = c;
        animator.start();
    }

    public void showNext() {
        if (getComponentCount() < 2)
            return;

        int i = 1;
        for (; i < getComponentCount(); i++) {
            if (getComponent(i) == current) {
                break;
            }
        }

        Component c = null;
        if (i == getComponentCount() - 1) {
            c = getComponent(1);
        } else {
            c = getComponent(i + 1);
        }

        show(c);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (policy == null || current == null || next == null) {
            return;
        }

        Graphics2D g1 = (Graphics2D) g;
        Graphics2D g2 = (Graphics2D) g.create();

        boolean current2Next = policy.transform(g1, g2, ratio, getWidth(), getHeight());

        if (current2Next) {
            crp.paintComponent(g2, current, this, 0, 0, getWidth(), getHeight(), true);
            crp.paintComponent(g1, next, this, 0, 0, getWidth(), getHeight(), true);
        } else {
            crp.paintComponent(g1, next, this, 0, 0, getWidth(), getHeight(), true);
            crp.paintComponent(g2, current, this, 0, 0, getWidth(), getHeight(), true);
        }
        g2.dispose();
    }

    class Layout extends AbstractLayout {

        @Override
        public Dimension preferredLayoutSize(Container parent) {
            return current != null && current.isVisible() ? current.getPreferredSize() : new Dimension(0, 0);
        }

        @Override
        public void layoutContainer(Container parent) {
            synchronized (parent.getTreeLock()) {
                Insets insets = parent.getInsets();
                int x = insets.left;
                int y = insets.top;
                int width = parent.getWidth() - insets.left - insets.right;
                int height = parent.getHeight() - insets.top - insets.bottom;

                if (current != null && current.isVisible()) {
                    current.setBounds(x, y, width, height);
                }
            }
        }
    }
}
